﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tomahawk.Resources.Materials;
using System.IO;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Tomahawk.Runtime.Rendering.Materials
{
    public class MaterialManager
    {
        #region Properties
        protected class EffectsTable : Dictionary<string, SASEffect> 
        {
            public EffectsTable() : base(StringComparer.OrdinalIgnoreCase) { }
        };
        protected Dictionary<SASEffectCollection, EffectsTable> collections = new Dictionary<SASEffectCollection, EffectsTable>();
        #endregion

        #region Initialization
        public MaterialManager()
        {
            MaterialReader.OnInitEffectCollection += new MaterialReader.InitSASEffectCollectionDelegate(OnInitSASEffectCollection);
        }

        public bool OnInitSASEffectCollection(SASEffectCollection collection)
        {
            collection.OnDispose += new SASEffectCollection.OnDisposedDelegate(collection_OnDispose);
            return true;
        }

        void collection_OnDispose(SASEffectCollection collection)
        {
            collections.Remove(collection);
        }
        #endregion

        public void Clear()
        {
            collections.Clear();
        }

        #region Parameters Templates
        protected bool ApplyParameterTemplate(SASMaterialTemplateParameter template, SASEffect effect, string rootPath, ContentManager contentManager)
        {
            bool result = true;
            SASParameter parameter = effect.Parameters[template.name];

            if (parameter != null)
            {
                switch (template.type)
                {
                    case SASMaterialTemplateParameter.Type.Bool: parameter.SetBoolValue(ref template.boolValue); break;
                    case SASMaterialTemplateParameter.Type.Int: parameter.SetIntValue(ref template.intValue); break;
                    case SASMaterialTemplateParameter.Type.Float: parameter.SetFloatValue(ref template.floatValue); break;
                    case SASMaterialTemplateParameter.Type.String: parameter.SetStringValue(ref template.stringValue); break;
                    case SASMaterialTemplateParameter.Type.Vector: parameter.SetVectorValue(ref template.vectorValue); break;
                    case SASMaterialTemplateParameter.Type.Texture: 
                        Texture texture = contentManager.Load<Texture>(Path.Combine(rootPath, template.textureValue));
                        parameter.SetTextureValue(ref texture); break;
                    default: throw new Exception("Unknown parameter type");
                }
            }

            return result;
        }
        #endregion

        #region Loading
        protected T Build<T>(string rootPath, SASEffectTemplate template, ContentManager contentManager) where T : SASEffect, new()
        {
            string effectFile = Path.Combine(rootPath, template.effectName);

            T sasEffect = new T();
            sasEffect.Initialize(effectFile, contentManager);

            foreach (SASMaterialTemplateParameter parameter in template.parameters)
            {
                ApplyParameterTemplate(parameter, sasEffect, rootPath, contentManager);
            }

            return sasEffect;
        }

        public SASMaterial Load<T>(string name, ContentManager contentManager) where T : SASEffect, new()
        {
            if (name == null)
            {
                return null;
            }

            SASMaterial material = null;
            string resourceName = name;
            string[] tokens = resourceName.Split(new char[] { '@' });
            if (tokens.Count() == 2)
            {
                SASEffectCollection collection = contentManager.Load<SASEffectCollection>(tokens[0]);
                if (collection != null)
                {
                    string effectName = tokens[0];
                    string templateName = tokens[1];
                    string rootPath = Path.GetDirectoryName(effectName);

                    EffectsTable effects = null;
                    if (!collections.TryGetValue(collection, out effects))
                    {
                        effects = new EffectsTable();
                        collections.Add(collection, effects);
                    }

                    SASEffect effect = null;
                    if (!effects.TryGetValue(resourceName, out effect))
                    {
                        SASEffectTemplate template = null;
                        if (collection.EffectTemplates.TryGetValue(templateName, out template))
                        {
                            effect = Build<T>(rootPath, template, contentManager);
                        }

                        effects.Add(resourceName, effect);
                    }

                    if (effect != null)
                    {
                        material = new SASMaterial(effect);
                    }
                }
            }

            return material;
        }

        #endregion
    }
}
